package com.mvc.my.root.handler;





import com.mvc.my.root.annotation.ResponseBody;
import com.mvc.my.root.annotation.ResponseView;
import com.mvc.my.root.utils.ResponseType;

import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;

/**
 * 类说明：
 * 1.内部类Mapping对象（此对象用来存放url指定controller类的实例化对象，controller映射的方法，返回的信息类型：视图或字符信息）
 * 2.一个hashmap，存放properties文件中指定controller类的方法method的urlpattern（此urlpattern存放于声明的注解ResponseBody、ResponseView的属性value中）
 * 3.get方法，用于获取hashmap中存放的Mapping映射对象；（通过hashmap的key获取，key值即是method的annotation的value值）
 * 4.load方法，将反射获取的属性文件流加载到properties对象中，获取到properties的value集合，
 * 遍历集合，创建集合类的实例化对象，遍历集合类的方法，遍历方法的注解，如果发现ResponseBody、ResponseView，
 * 则对其是否重复加载判断，若第一次加载，将其ResponseBody、ResponseView中存储的值，作为key，封装实例对象，方法，ResponseType为Mapping，
 * 将所有的mapping存放于hashmap中供读取
 *
 * 结构：
 * hashmap集合
 * mapping内部类
 * get读取方法
 * load加载方法
 */
public class HandlerMapping {
    /**
     * create a hashmap to store url mapping methods
     */
    private static Map<String, MVCMapping> MVCdata = new HashMap<>();

    /**
     * get the inner class mapping methods by inputting url
     *
     * @param url
     * @return
     */
    public static MVCMapping getMapping(String url) {
        return MVCdata.get(url);
    }

    /**
     * load the file resource to init package methods
     *
     * @param is
     */
    public static void load(InputStream is) {
        Properties pps = new Properties();
        try {
            if (is != null) {
                pps.load(is);
            }else {
                throw  new RuntimeException("Applications.properties 为空");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        Collection<Object> values = pps.values();
        for (Object o :
                values) {
            Class<?> classForName = null;
            Object newInstance = null;
            try {
                classForName = Class.forName((String) o);
                newInstance = classForName.getConstructor().newInstance();
            } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
                e.printStackTrace();
            }
            Method[] methods = classForName.getMethods();
            for (Method method :
                    methods) {
                Annotation[] annotations = method.getAnnotations();
                if (annotations != null) {
                    for (Annotation as :
                            annotations) {
                        if (as instanceof ResponseBody) {
                            MVCMapping mvcMapping = new MVCMapping(newInstance, method, ResponseType.TEXT);
                            MVCMapping put = MVCdata.put(((ResponseBody) as).value(), mvcMapping);
                            if (put != null) {
                                //存在了重复的请求地址
                                throw new RuntimeException("请求地址重复："+((ResponseBody) as).value());
                            }
                        }else if (as instanceof ResponseView) {
                            MVCMapping mvcMapping = new MVCMapping(newInstance, method, ResponseType.VIEW);
                            MVCMapping put = MVCdata.put(((ResponseView) as).value(), mvcMapping);
                            if (put != null) {
                                //存在了重复的请求地址
                                throw new RuntimeException("请求地址重复："+((ResponseView) as).value());
                            }
                        }

                    }
                }

            }
        }

    }

    /**
     * mvc mapping object template
     */
    public static class MVCMapping {
        private Object obj;
        private Method method;
        private ResponseType responseType;

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (!(o instanceof MVCMapping)) return false;
            MVCMapping that = (MVCMapping) o;
            return Objects.equals(getObj(), that.getObj()) &&
                    Objects.equals(getMethod(), that.getMethod()) &&
                    getResponseType() == that.getResponseType();
        }

        @Override
        public int hashCode() {
            return Objects.hash(getObj(), getMethod(), getResponseType());
        }

        @Override
        public String toString() {
            return "MVCMapping{" +
                    "obj=" + obj +
                    ", method=" + method +
                    ", responseType=" + responseType +
                    '}';
        }

        public MVCMapping() {
        }

        public MVCMapping(Object obj, Method method, ResponseType responseType) {
            this.obj = obj;
            this.method = method;
            this.responseType = responseType;
        }

        public Object getObj() {
            return obj;
        }

        public void setObj(Object obj) {
            this.obj = obj;
        }

        public Method getMethod() {
            return method;
        }

        public void setMethod(Method method) {
            this.method = method;
        }

        public ResponseType getResponseType() {
            return responseType;
        }

        public void setResponseType(ResponseType responseType) {
            this.responseType = responseType;
        }
    }
}
